#include <cstdlib>
#include <cassert>
#include <list>
#include <string>
#include <vector>
#include <deque>
#include <thread>

#include "mylist.h"
#include "uring.h"
#include "epoch.h"
#include "tlbtree/include/tlbtree.h"
#include "morphtree/include/morphtree.h"
#include "kvalloc/include/kvalloc.h"

namespace dnskv {

#define ENABLE_HOTINDEX
#define BG_RECLAIM

// currently DNSKV only supports keys less than 8 bytes
// Note that we need to make sure 
#ifndef KEYTYPE
    using _key_t = uint64_t;
#else
    using _key_t = KEYTYPE;
#endif

using std::string;
using std::vector;
using kvalloc::FileAddr;
using kvalloc::RWLock;

enum ReqType {PUT, UPDATE, DELETE};

// Request meta data: 8 Bytes
struct Request {
    uint8_t op    : 6;
    uint8_t stale : 1;
    uint8_t hot   : 1;
    RWLock  rwlock;
    uint16_t crc;
    uint32_t val_size;
    char keyvalue[0];
    // key size: 8 Bytes
    // val size: variable
    inline uint32_t Length() {
        return sizeof(Request) + 8 + val_size;
    }
};

struct DNSOpt {
    std::string dbdir;   // data directory
    std::string nvmdir;  // NVM directory 

    uint64_t ssdsize  = 4096UL * 1024 * 1024; // SSD data file size  : byte unit
    uint64_t nvmsize  = 512UL * 1024 * 1024;  // NVM capaicty        : byte unit
    uint64_t dramsize = 128UL * 1024 * 1024;  // DRAM capacity       : byte unit
    int avgsize       = 256;                  // average item size
};

/// @brief DNSKV is a KVStore designed for DRAM/NVM/SSD.
/* DNSKV provide synchronous KV interfaces and offers high performance via three components: 
    1. DRAM KV-level caches of hot items
    2. NVM Global B+-tree that index every item
    3. SSD Slab Allocation that reduces WAF and RAF
*/
class DNSKV {
private:
    morphtree::Morphtree * hot_index_;
    tlbtree::TLBtree * global_index_;
    kvalloc::KVAlloc * allocator_;

    int log_fd_;
    int db_fd_;
    uint64_t log_offset_;
    uint64_t reclaim_offset_;
    bool global_ending_;
    std::thread reclaim_thread_;
    
    LinkList<Request *> hot_items_;
    std::atomic<uint32_t> hot_num_;
    kvalloc::SpinLock evict_mu_;

    // maximium hot item number
    uint64_t max_hot_num_;

public:
    DNSKV(const DNSOpt &);
    
    ~DNSKV();

    // insert a key value pair, sync or not to storage
    void Put(const _key_t k, const string & v, bool sync);

    // update a key value pair, sync or not to storage
    bool Update(const _key_t k, const string & v, bool sync);

    // delete the value associated with key k, sync or not to storage
    bool Delete(const _key_t k, bool sync);

    // get the value associated with key k
    bool Get(const _key_t k, string & v);

    // range query key value pairs with 
    int Scan(const _key_t k, int len, vector<string> & out);

private:
    FileAddr Append(Request * req, bool sync);

    void EvictBatch(int num = 0);

    void LogReclaim();
};

} // namespace dnskv